package com.neuronbit.businessflow.runtime;

import com.neuronbit.businessflow.runtime.exec.ExecutedFlowInstance;
import com.neuronbit.businessflow.runtime.exec.FlowExecutionStatus;
import com.neuronbit.businessflow.runtime.exec.LockStatus;
import lombok.Getter;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * The type Flow.
 */
public abstract class Flow<T extends FlowContext, R> {
    private final String name;
    @Getter
    private final String description;
    /**
     * The constant DEFAULT_EXECUTOR.
     */
    public static final ExecutorService DEFAULT_EXECUTOR =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4);
    private ExecutorService executor;
    @Getter
    private final StepBeanProvider stepBeanProvider;
    @Getter
    private final String id;

    /**
     * Instantiates a new Flow.
     *
     * @param name        the name
     * @param description the description
     */
    public Flow(String name, String description, StepBeanProvider stepBeanProvider, String id) {
        this.name = name;
        this.description = description;
        this.stepBeanProvider = stepBeanProvider;
        this.id = id;
    }

    public abstract R reRun(T context, boolean force) throws Exception;

    protected void initAndLock(T context) throws Exception {
        ExecutedFlowInstance instance = new ExecutedFlowInstance();
        context.setExecutionId(UUID.randomUUID().toString());
        instance.setExecutionId(context.getExecutionId());
        instance.setFlowId(this.getId());
        instance.setFlowName(this.getName());
        instance.setFlowTypeName(this.getClass().getName());
        instance.setContext(GsonUtil.toJson(context));
        instance.setContextTypeName(context.getClass().getName());
        instance.setStatus(FlowExecutionStatus.INIT);
        instance.setLockStatus(LockStatus.LOCKED);
        instance.setStartTime(LocalDateTime.now());
        try {
            this.stepBeanProvider.getRepository().initAndLock(instance);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Sets executor.
     *
     * @param executor the executor
     */
    public void setExecutor(ExecutorService executor) {
        this.executor = executor;
    }

    /**
     * Gets executor.
     *
     * @return the executor
     */
    public ExecutorService getExecutor() {
        if (null == executor) {
            return DEFAULT_EXECUTOR;
        }
        return executor;
    }

    /**
     * Gets name.
     *
     * @return the name
     */
    public String getName() {
        return name;
    }
}
